from django.db import models
import mechanize
import traceback
import datetime
from threading import Thread
import socket
from urllib2 import quote
socket.setdefaulttimeout(7)

class HealthCheck(models.Model):
	start_at = models.DateTimeField()
	stop_at = models.DateTimeField(blank=True)
	
	def check_all(self, batch=False):
		if batch:
			now = datetime.datetime.now()
			if now < self.start_at: return
			if self.stop_at and now >= self.stop_at: return
		
		for group in Group.objects.all():
			group.check_all()

class Group(models.Model):
	name = models.CharField(max_length=200)

	def check_all(self):
		for senario in self.senario_set.filter(enabled=True):
			senario.check_all()
			
	def __unicode__(self):
		return self.name
	
class Server(models.Model):
	address = models.CharField(max_length=100)
	group = models.ForeignKey(Group)
	
	def __unicode__(self):
		return '[%s] %s' % (self.group.name, self.address) 
	

class Senario(models.Model):
	HELP_TEXT = '''
for example:
<pre>
browser.open(url_prefix + '/')
link = browser.links().next()
browser.follow_link(link)
assert 'buy_button' in browser.response().read()
</pre>
'''
	group = models.ForeignKey(Group)
	title = models.CharField(max_length=200)
	script = models.TextField(help_text=HELP_TEXT)
	timeout = models.IntegerField(default=3)
	enabled = models.BooleanField(db_index=True)
	created_at = models.DateTimeField(auto_now_add=True)
	updated_at = models.DateTimeField(auto_now=True)
	
	class CheckThread(Thread):
		def __init__(self, senario, server):
			self.senario = senario
			self.server = server
			self.timeout = False
			Thread.__init__(self)
	
		def run(self):
			try:
				browser = mechanize.Browser()
				url_prefix = 'http://' + self.server.address
				exec self.senario.script.replace('\r', '\n')
				if self.timeout:
					SenarioLog.timeout(self.senario, self.server)
				else:
					SenarioLog.ok(self.senario, self.server)
			except:
				if self.timeout:
					SenarioLog.timeout(self.senario, self.server, traceback.format_exc())
				else:
					SenarioLog.error(self.senario, self.server, traceback.format_exc())
			finally:
				browser.close()
				

	def check(self, server):
		t = self.CheckThread(self, server)
		t.start()
		t.join(self.timeout)
		if t.isAlive(): t.timeout = True
			
	def check_all(self):
		for server in self.group.server_set.all():
			self.check(server)

	def latest_log(self, server):
		logs = self.senariolog_set.filter(server=server)
		if logs.count():
			return logs.latest('created_at')
		else:
			return SenarioLog(senario=self, server=server, status='not checked', created_at=datetime.datetime.fromtimestamp(0))
	
	def __unicode__(self):
		return '[%s] %s' % (self.title, self.script)

class SenarioLog(models.Model):
	senario = models.ForeignKey(Senario)
	server = models.ForeignKey(Server)
	status = models.CharField(max_length=100)
	message = models.TextField(null=True)
	created_at = models.DateTimeField(auto_now_add=True, db_index=True)
	
	@staticmethod
	def timeout(senario, server, message=''):
		log = senario.senariolog_set.create(server=server, status="TIMEOUT", message=message)
		logs = senario.senariolog_set.filter(server=server).order_by('-created_at')[:3]
		if len(filter(lambda log: log.status == 'TIMEOUT', logs)) >= 3:
			Notification.notify(log)
			
	@staticmethod
	def ok(senario, server, message=''):
		senario.senariolog_set.create(server=server, status="OK", message=message)

	@staticmethod
	def error(senario, server, message):
		log = senario.senariolog_set.create(server=server, status="ERROR", message=message)
		Notification.notify(log)
			
	def __unicode__(self):
		return "%s %s %s %s" % (self.senario.title, self.server.address, self.status, self.created_at) 

class Notification(models.Model):
	group = models.ForeignKey(Group)
	notify_url = models.CharField(max_length=1000)

	@staticmethod
	def notify(log):
		browser = mechanize.Browser()
		for noti in Notification.objects.filter(group=log.senario.group):
			message = u"[HealthCheck] %s %s %s %s" % (log.status, log.senario.group, log.server.address, log.senario.title)
			browser.open(noti.notify_url + quote(message.encode("utf8")))
		browser.close()

	def __unicode__(self):
		return self.group.name + ' ' + self.notify_url